package com.idempotent.demo.service;

import com.idempotent.demo.dto.OrderDTO;
import com.idempotent.demo.entity.Order;
import com.idempotent.demo.exception.IdempotentException;
import com.idempotent.demo.repository.OrderRepository;
import com.idempotent.demo.util.TokenUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.concurrent.TimeUnit;

@Service
public class IdempotentService {

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private TokenUtil tokenUtil;

    @Value("${idempotent.lock.wait-time:3}")
    private long lockWaitTime; // Redisson锁等待时间（秒）


    // ------------------------------ 1. Redis Token实现幂等性 ------------------------------
    /**
     * 生成Redis Token（供客户端获取）
     */
    public String generateRedisToken() {
        String token = tokenUtil.generateToken();
        // 存储Token到Redis
        redisTemplate.opsForValue().set(
                "token:redis:" + token,
                "UNUSE",
                tokenUtil.getTokenExpire(),
                TimeUnit.SECONDS
        );
        return token;
    }

    /**
     * Redis Token方式创建订单
     */
    @Transactional
    public Order createOrderWithRedisToken(String token, OrderDTO dto) {
        // 1. 验证Token是否有效
        String redisKey = "token:redis:" + token;
        String value = redisTemplate.opsForValue().get(redisKey);
        if (value == null) {
            throw new IdempotentException("Token已失效或重复请求");
        }

        // 2. 原子性删除Token（确保唯一执行）
        Boolean deleted = redisTemplate.delete(redisKey);
        if (!deleted) {
            throw new IdempotentException("重复请求，请稍后再试");
        }

        // 3. 执行业务逻辑
        return createOrder(dto);
    }


    // ------------------------------ 2. Redisson分布式锁实现幂等性 ------------------------------
    /**
     * 生成Redisson Token（供客户端获取）
     */
    public String generateRedissonToken() {
        String token = tokenUtil.generateToken();
        // 存储Token到Redis
        redisTemplate.opsForValue().set(
                "token:redisson:" + token,
                "UNUSE",
                tokenUtil.getTokenExpire(),
                TimeUnit.SECONDS
        );
        return token;
    }

    /**
     * Redisson分布式锁方式创建订单
     */
    @Transactional
    public Order createOrderWithRedisson(String token, OrderDTO dto) {
        String lockKey = "lock:redisson:" + token;
        RLock lock = redissonClient.getLock(lockKey);
        boolean locked = false;

        try {
            // 1. 尝试获取锁
            locked = lock.tryLock(lockWaitTime, -1, TimeUnit.SECONDS);
            if (!locked) {
                throw new IdempotentException("操作频繁，请稍后再试");
            }

            // 2. 验证Token是否有效
            String redisKey = "token:redisson:" + token;
            String value = redisTemplate.opsForValue().get(redisKey);
            if (value == null) {
                throw new IdempotentException("Token已失效或重复请求");
            }

            // 3. 删除Token（标记为已使用）
            redisTemplate.delete(redisKey);

            // 4. 执行业务逻辑
            return createOrder(dto);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IdempotentException("获取锁中断");
        } finally {
            // 5. 释放锁
            if (locked && lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }


    // ------------------------------ 3. 数据库唯一约束实现幂等性 ------------------------------
    /**
     * 数据库唯一约束方式创建订单
     */
    @Transactional
    public Order createOrderWithDbUnique(OrderDTO dto) {
        try {
            // 直接插入，若order_no重复会触发唯一约束异常
            return createOrder(dto);
        } catch (Exception e) {
            // 捕获唯一约束异常
            if (e.getMessage().contains("unique constraint") || e.getMessage().contains("duplicate key")) {
                throw new IdempotentException("订单已存在（重复提交）");
            }
            throw new IdempotentException("创建订单失败", e);
        }
    }


    // ------------------------------ 4. 数据库Version乐观锁实现幂等性 ------------------------------
    /**
     * 乐观锁方式更新订单状态
     */
    @Transactional
    public Order updateOrderWithVersion(Long orderId, Integer newStatus, Integer currentVersion) {
        // 1. 查询订单
        Order order = orderRepository.findById(orderId)
                .orElseThrow(() -> new IdempotentException("订单不存在"));

        // 2. 验证版本号
        if (!order.getVersion().equals(currentVersion)) {
            throw new IdempotentException("数据已被修改，请刷新后重试");
        }

        // 3. 更新状态
        order.setStatus(newStatus);
        return orderRepository.save(order);
    }


    // ------------------------------ 5. 请求唯一ID（requestId）方式实现幂等性 ------------------------------
    /**
     * 基于requestId的幂等实现
     */
    @Transactional
    public Order createOrderWithRequestId(String requestId, OrderDTO dto) {
        // 1. 定义Redis键
        String redisKey = "requestId:" + requestId;
        
        // 2. 尝试将requestId存入Redis
        Boolean isFirstRequest = redisTemplate.opsForValue().setIfAbsent(
                redisKey, 
                "PROCESSING",
                tokenUtil.getTokenExpire(), 
                TimeUnit.SECONDS
        );
        
        if (Boolean.FALSE.equals(isFirstRequest)) {
            // 重复请求
            String result = redisTemplate.opsForValue().get(redisKey);
            if ("SUCCESS".equals(result)) {
                // 已处理成功，返回已有订单
                return orderRepository.findByOrderNo(dto.getOrderNo())
                        .orElseThrow(() -> new IdempotentException("订单已存在，但查询失败"));
            } else {
                throw new IdempotentException("请求已受理，请不要重复提交");
            }
        }
        
        try {
            // 3. 首次请求，执行业务逻辑
            Order order = createOrder(dto);
            
            // 4. 业务成功后，更新Redis状态为成功
            redisTemplate.opsForValue().set(redisKey, "SUCCESS", tokenUtil.getTokenExpire(), TimeUnit.SECONDS);
            return order;
        } catch (Exception e) {
            // 5. 业务失败，删除requestId，允许重试
            redisTemplate.delete(redisKey);
            throw new IdempotentException("业务处理失败，请重试", e);
        }
    }


    // ------------------------------ 公共方法 ------------------------------
    /**
     * 创建订单的业务逻辑
     */
    private Order createOrder(OrderDTO dto) {
        // 简单校验
        if (dto.getOrderNo() == null || dto.getUserId() == null || dto.getAmount() == null) {
            throw new IdempotentException("订单信息不完整");
        }
        
        Order order = new Order();
        order.setOrderNo(dto.getOrderNo());
        order.setUserId(dto.getUserId());
        order.setAmount(dto.getAmount());
        order.setStatus(0); // 初始状态：待支付
        return orderRepository.save(order);
    }
}
    